#include "Postscript_Stream.h"
#include <math.h>
#include "Log.h"

Postscript_Stream::Postscript_Stream(const std::string& filename) {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    page_number = 1;
    open(filename);
}

Postscript_Stream::~Postscript_Stream() {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    close();
}

void Postscript_Stream::open(const std::string& filename) {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    page_width_inches  = 8;
    page_height_inches = 10.5;
    points_per_inch    = 72;
    margin = page_width_inches * 0.05 * points_per_inch;

    ps_file.open(filename.c_str());
    header();

    set_bounds(0,0, 10,10);
    set_dot_size(5);
    set_font_size(10);
}

void Postscript_Stream::close() {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    trailer();
    ps_file.close();
}

void Postscript_Stream::header() {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    int clip_x = (int)(page_width_inches * points_per_inch);
    int clip_y = (int)(page_height_inches * points_per_inch);
    ps_file << "%!PS-Adobe-2.0\n";
    ps_file << "%%Creator: util/Postscript_Stream\n";
    ps_file << "%%DocumentFonts: Helvetica\n";
    ps_file << "%%BoundingBox: 0 0 " << clip_x << " " << clip_y << "\n";
    ps_file << "%%EndComments\n";
    ps_file << "[] 0 setdash\n";
    ps_file << "1 setlinewidth\n";
    ps_file << "newpath\n";
    ps_file << "0 0 " << clip_x << " " << clip_y << " rectclip\n";
    ps_file << "/Helvetica findfont 16 scalefont setfont\n";
    ps_file << "%%Page: " << page_number << " " << page_number << "\n";
}

void Postscript_Stream::trailer() {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    ps_file << "showpage\n";
    ps_file << "%%Trailer\n";
}

void Postscript_Stream::set_bounds(double BLx, double BLy,
                                   double TRx, double TRy) {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    this->BLx = BLx;
    this->BLy = BLy;
    this->TRx = TRx;
    this->TRy = TRy;

    double page_aspect_ratio = page_height_inches / page_width_inches;
    double w = TRx - BLx;
    double h = TRy - BLy;
    double bounds_aspect_ratio = h / w;
    if (bounds_aspect_ratio > page_aspect_ratio)
        wcs_to_dcs_scale = (points_per_inch * page_height_inches - margin*2)
            / (TRy - BLy);
    else
        wcs_to_dcs_scale = (points_per_inch * page_width_inches - margin*2)
            / (TRx - BLx);

//    LOG(SPLIT) << "(w h)=(" << w << " " << h << ") AR=" << bounds_aspect_ratio << " scale=" << wcs_to_dcs_scale;
}

void Postscript_Stream::end_page() {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    ps_file << "showpage\n";
    page_number++;
    ps_file << "%%Page: " << page_number << " " << page_number << "\n";
}

void Postscript_Stream::draw_line(double x1, double y1,
                                  double x2, double y2) {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    double x1d, y1d, x2d, y2d;

    wcs_to_dcs(x1,y1, x1d, y1d);
    wcs_to_dcs(x2,y2, x2d, y2d);

    ps_file << "newpath ";
    ps_file << x1d << " " << y1d << " moveto ";
    ps_file << x2d << " " << y2d << " lineto ";
    ps_file << "stroke\n";
}

void Postscript_Stream::join_dots(double x1, double y1,
                                  double x2, double y2) {

    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    double dx = x2 - x1;
    double dy = y2 - y1;
    double len = sqrt(dx*dx + dy*dy);
    if (len > 0) {
        dx /= len;
        dy /= len;
        dx *= dot_size_wcs/2;
        dy *= dot_size_wcs/2;
        draw_line(x1 + dx, y1 + dy,
                  x2 - dx, y2 - dy);
    }
}

void Postscript_Stream::draw_dot(double x, double y, bool solid) {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    double xd,yd;

    wcs_to_dcs(x,y, xd,yd);
    ps_file << "newpath " << xd << " " << yd << " " << dot_size
           << " 0 360 arc ";
    if (solid)
        ps_file << "fill\n";
    else
        ps_file << "stroke\n";
}

void Postscript_Stream::draw_caption(const std::string& label) {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    draw_label(label, BLx, BLy);
}

void Postscript_Stream::draw_label(const std::string& label,
                                   double x, double y) {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    double xd,yd;

    wcs_to_dcs(x,y, xd,yd);
    ps_file << xd << " " << yd << " moveto (" << label << ") show\n";
}

void Postscript_Stream::label_dot(const std::string& label,
                                  double x, double y) {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    x += dot_size_wcs * 4;
    y += dot_size_wcs * 4;
    draw_label(label, x,y);
}

void Postscript_Stream::set_font_size(int points) {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    font_size = points;
    ps_file << "/Helvetica findfont " << font_size << " scalefont setfont\n";
}

void Postscript_Stream::set_dot_size(int points) {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    dot_size = points;
    dot_size_wcs = dot_size / wcs_to_dcs_scale;
}

void Postscript_Stream::wcs_to_dcs(double xw, double yw,
                                   double& xd, double& yd) {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    xd = (xw - BLx) * wcs_to_dcs_scale + margin;
    yd = (yw - BLy) * wcs_to_dcs_scale + margin;

//    LOG(SPLIT) << "BL=(" << BLx << " " << BLy << ") margin=" << margin;
//    LOG(SPLIT) << "wcs_to_dcs (x y)=(" << xw << " " << yw << ") (xd yd)=(" << xd << " " << yd << ")";

}

void Postscript_Stream::flush() {
    if (!(LOG_DETAIL_BIT_PS & Log::active_details))
        return;

    ps_file.flush();
}
